-- Viral Resistance - T-Engine 4 Module
-- Copyright (C) 2010 Mikolai Fajer
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
--
-- Mikolai Fajer "yufra"
-- mfajer@gmail.com

require "engine.class"
local Astar = require "engine.Astar"

module(..., package.seeall, class.inherit(engine.GameTurnBased, engine.interface.GameTargeting))

function _M:init(t)
end

function _M:run()
	if not game.level then return end
	-- Find all of the spawn sites
	local _, director_spots = game.level:pickSpot({type="director", subtype="spawn"})
	for i, spot in ipairs(director_spots) do
		-- Check the percentage
		if spot.count and (spot.count > 0) and rng.percent(33) then
			print("[[SPAWN]] Spawn attempt (turn ", game.turn, ") from... ", spot.x, " :==: ", spot.y)
			local spawned = game.zone:makeEntity(game.level, "actor", {type="zombie"}, nil, true)
			-- Find the closest out of LOS along an Astar path
			local a = Astar.new(game.level.map, spawned)
			local path = a:calc(spot.x, spot.y, game.player.x, game.player.y)
			local spawn_x, spawn_y
			while path and (#path > 0) do
				-- Check if the player can see this tile
				local tile_dmap = game.player:distanceMap(path[1].x, path[1].y)
				if tile_dmap and (tile_dmap >= game.player.fov_last_turn) then
--					print("[[SPAWN]] Bad tile (dmap ", tile_dmap, ")... ", path[1].x, " :==: ", path[1].y)
					break
				end
--				print("[[SPAWN]] Ok tile (dmap ", tile_dmap, ")... ", path[1].x, " :==: ", path[1].y)
				spawn_x, spawn_y = path[1].x, path[1].y
				table.remove(path, 1)
			end
			-- If that isn't possible, search nearby a spawn site
			if not spawn_x or not spawn_y then
				local check = function(tx, ty)
					local block = game.level.map:checkEntity(tx, ty, engine.Map.TERRAIN, "block_move", spawned, nil, true)
					local dmap = game.player:distanceMap(tx, ty)
					return not block and (not dmap or (dmap < game.player.fov_last_turn))
				end
				local possible_tiles = {}
				local apply = function(tx, ty)
					table.insert(possible_tiles, {x=tx, y=ty})
				end
				self:floodFill(spot.x, spot.y, check, apply)
				if #possible_tiles > 0 then
					local selection = rng.table(possible_tiles)
					spawn_x, spawn_y = selection.x, selection.y
--					print("[[SPAWN]] Selected from flood fill... ", spawn_x, " :==: ", spawn_y)
				end
			else
--				print("[[SPAWN]] Selected from Astar... ", spawn_x, " :==: ", spawn_y)
			end
			if spawn_x and spawn_y then
				game.zone:addEntity(game.level, spawned, "actor", spawn_x, spawn_y)
				spot.count = spot.count - 1
			end
		end
	end
end

-- A flood-fill
function _M:floodFill(start_x, start_y, check, apply)
	local flood_queue = {}
	flood_queue.queue = {}
	flood_queue.history = {}
	function flood_queue:insert(value)
		if not self.history[value.x] then
			self.history[value.x] = {}
		end
		if self.history[value.x][value.y] then return end
		self.history[value.x][value.y] = true
		table.insert(self.queue, value)
	end
	function flood_queue:remove()
		return table.remove(self.queue)
	end
	flood_queue:insert{x=start_x, y=start_y}
	while #flood_queue.queue > 0 do
		local current_tile = flood_queue:remove()
		-- Check that the tile exists in the map and the user-defined check
		if game.level.map(current_tile.x, current_tile.y) and check(current_tile.x, current_tile.y) then
			if apply then apply(current_tile.x, current_tile.y) end
			flood_queue:insert({x=current_tile.x-1, y=current_tile.y})
			flood_queue:insert({x=current_tile.x+1, y=current_tile.y})
			flood_queue:insert({x=current_tile.x, y=current_tile.y-1})
			flood_queue:insert({x=current_tile.x, y=current_tile.y+1})
		end
	end
	return flood_queue.history
end